import time
import json
import traceback
from typing import Callable
from fastapi import Request, Response
from starlette.background import BackgroundTasks
from starlette.responses import StreamingResponse

async def _log_to_database(
    request: Request,
    response: Response,
    request_data: dict,
    response_data: dict,
    duration: float,
    success: bool,
    error_message: str = None
):
    """
    This function runs in the background.
    It handles all the logic for parsing the request and writing to the DB.
    """
    # --- Get Request Info ---
    method = request.method
    path = request.url.path
    
    # --- Get User Info ---
    user_id = 0
    username = "anonymous"
    try:
        if request.headers.get("authorization"):
            from app.core.security import decode_access_token
            from app.modules.users.services import UserService
            
            auth_header = request.headers.get("authorization", "")
            if auth_header.startswith("Bearer "):
                token = auth_header.split(" ")[1]
                payload = decode_access_token(token)
                if payload and "sub" in payload:
                    _username = payload.get("sub")
                    user = await UserService.get_by_username(_username)
                    if user:
                        user_id = user.id
                        username = user.username
    except Exception:
        pass # Ignore errors, log as anonymous

    # --- Get Other Info ---
    ip = request.headers.get("x-forwarded-for") or request.client.host
    user_agent = request.headers.get("user-agent")
    status_code = response.status_code
    
    # --- Parse Module/Action ---
    if path.startswith("/api/"):
        parts = path[5:].strip("/").split("/")
        module = parts[0] if parts else "unknown"
        action_map = {"GET": "查询", "POST": "创建", "PUT": "更新", "PATCH": "修改", "DELETE": "删除"}
        action = action_map.get(method, method.lower())
    else:
        module, action = "unknown", "unknown"

    # --- Write to Database ---
    try:
        from app.modules.logs.models import OperationLogModel
        await OperationLogModel.create_log(
            user_id=user_id,
            username=username,
            module=module,
            action=action,
            method=method,
            path=path,
            ip=ip,
            user_agent=user_agent,
            request_data=request_data,
            response_data=response_data,
            status_code=status_code,
            duration=round(duration, 3),
            success=success,
            error_message=error_message,
        )
    except Exception as e:
        print(f"Background logger failed: {e}\n{traceback.format_exc()}")


async def logging_middleware(request: Request, call_next: Callable):
    """
    High-performance logging middleware using BackgroundTasks.
    """
    start_time = time.time()

    # --- Get Request Body ---
    request_data = None
    try:
        if request.method in ["POST", "PUT", "PATCH"]:
            body = await request.body()
            if body:
                request_data = json.loads(body.decode())
            # We need to recreate the body stream for the actual endpoint
            request._receive = lambda: {"type": "http.request", "body": body}
    except Exception:
        pass # Ignore body parsing errors

    # --- Process Request and Get Response ---
    response = None
    error_message = None
    success = False
    try:
        response = await call_next(request)
        success = True
    except Exception as e:
        # If an error occurs, we create a generic 500 response
        # so we can still log the failure.
        error_message = str(e)
        response = Response(status_code=500, content=json.dumps({"detail": "Internal Server Error"}))
        # Important: re-raise the exception so FastAPI's default handler can also process it
        raise e
    finally:
        duration = time.time() - start_time
        response_data = None

        if isinstance(response, StreamingResponse):
            # We cannot read the body of a streaming response
            response_data = {"detail": "Streaming Response"}
        else:
            try:
                # We need to access the raw body, not response.json()
                # as it might already be consumed.
                response_body = getattr(response, 'body', None)
                if response_body:
                    response_data = json.loads(response_body.decode())
            except Exception:
                pass # Ignore response body parsing errors

        # --- Create and attach background task ---
        # This is the key part: the task runs *after* the response is sent.
        if response:
            tasks = BackgroundTasks()
            tasks.add_task(
                _log_to_database,
                request=request,
                response=response,
                request_data=request_data,
                response_data=response_data,
                duration=duration,
                success=success,
                error_message=error_message,
            )
            # Attach the tasks to the response
            response.background = tasks
            
    return response 